expr shorthand for Tcl9

This page collects various suggestions from the Tcl 9.0 WishList about shorthands that one could use instead of the expr command. The original numbered suggestions were

34. GPS: I would like the math commands + - * / in the standard Tcl 9.0.

and

41. Larry Smith - some special syntax to invoke expr

the discussions of which follow below (slightly reordered). Two subsequent suggestions are

Lars H - extended $ substitution as expr shorthand

and

Martin Lemburg - special quotation to allow the bytecompiler to recognize expression

NEM 2009-03-20: I've created a patch for Jacl that implements (...) as shorthand for [expr {...}] at [L1 ]. It also includes some new commands as alternatives to if that use the syntax rather than building-in expr, so you can write things like:

proc fac n {
    when ($n <= 1) { return 1 }
    otherwise { return ($n * [fac ($n-1)]) }
}

It's all very experimental, and doesn't pass the Tcl test-suite: doesn't conflict with arrays (except the empty named array), but does conflict with some uses like [regexp (a|b|c...) ....]. Meant as a feasibility test and fun experiment rather than for serious use.

TCV 2009-03-20 Does [set xyzzy(1+2) ohai] set an array element or xyzzy3?

NEM Try it. It sets an array element. As for braces and quotes, the expr syntax is only valid at the start of a word.


Steve Bennett 1 Nov 2010 I experimented with adding expr shorthand to Jim with $(...). I know that some will complain that this conflicts with the empty array syntax in stooop, but Jim is less burdened with strict backwards compatibility. This is a very natural syntax, similar to bash $((...)) but less likely to conflict than plain (...). I think that it is a big win for readability and I am strongly inclined to keep it.

. set x $(3 + 4)
7
. incr y $(7 * [incr x])
56
. set z $("bb" in {aa bb cc})
1

Talk

GPS: I would like the math commands + - * / in the standard Tcl 9.0. Expr is awkward, and having to use braces to workaround the byte-code compiler sucks. The issue with this as I see it is whether to make them operate on longs/ints or doubles. How can we solve this?

Lars H: You can of course have them right here and now. Just define

proc - {term1 term2} {expr {$term1 - $term2}}
proc + {args} {
   set res [lindex $args 0]
   foreach arg [lrange $args 1 end] {set res [expr {$res + $arg}]}
   set res
}
proc * {args} {
   set res [lindex $args 0]
   foreach arg [lrange $args 1 end] {set res [expr {$res * $arg}]}
   set res
}
proc / {numer denom} {expr {$numer / $denom}}

and you'll be able to write things like

+ 1 2 [- 3 4] [* 5 6] [/ 7 8]

for 1+2+(3-4)+(5*6)+(7/8).

The idea that one should decide whether mathematical operations operates on "longs/ints" or "doubles" shows clear signs of too much C programming. In Tcl, everything is a string.

LV In addition, with Tcl 8.5 one has the tcl::mathop namespace, from which one can import the operators as commands as well. So this wishlist item seems, at first glance, to be fulfilled now, rather than in Tcl 9.0.

ZB 23.09.2008. Personally I couldn't understand, if someone finds the solutions proposed above as "not enough". The first one looks just obvious, and usage of namespace will be especially convenient in case of math-oriented procedures. Besides: although "expr" syntax isn't especially pretty, but it's not that ugly anyway; one can live with that. Not sure, is really that "search for new delimiters" (and so on) worthy any further efforts.

peterc 2998-09-24: I wouldn't think there'd be many (any?) scripts which name their procs solely as a number. Surely a nice simple way might be to test the proc name to see if it's numeric, then if so, send it to an expr-like math proc. Then you could have more eye-friendly commands like:

set a [12 + 5]

JJS 2009-05-14: I once wrote an application with some procs named as numbers. It received messages with a header consisting of a command code, a length, and data. Once it had a complete message, it invoked the command code as a proc, passing the data payload and some other info. To define a message handler you'd write something like

proc 12345 {data len chan} {...}

where 12345 was the command code. I was a relative newbie back then, but it worked.


Martin Lemburg 17.02.2003:

In Tcl, everything is a string.? No, definitely not!

For calculations it is definitely important to handle number values only with commands that don't change the internal tcl type to string, so the exact representation is not lost!

If you don't care, if you don't have to care for the internal representation, than you can act like [i]n Tcl, everything is a string.!

We have many matrices to be multiplied and so we have to care about the internal type and not decrease the calculation's precision!

RFox

The concept of an <em>exact representation</em> is nonesense. The best you get on any computing device is an approximate fixed precision representation. In Tcl, you can set the precision to be what you want it to be, but it's still the precision of a stringized - fixed point representation that is just as exact or inexact as the binary fixed precision representation.

Lars H: That everything is a string is one of the basic principles of Tcl. It is quite true that the internal representation of real numbers currently contains more information than the string representation -- this is the subject of the A real problem page -- but IMO this is a bug in Tcl. (It can alternatively be seen as a malfeature of the tcl_precision variable.) The correct way (used by e.g. Knuth for TeX) to handle this problem is rather to make sure that the string representation of a number contains enough digits to uniquely specify all bits in the internal representation. This will mean that you generally "cannot trust the last digit" and will see numbers written with more digits than "what is really there", but since this is always "inside the program" that should not be a problem; anything shown to a user should be explicitly formatted.

Finally, if you are interested in maximal precision then you should probably read what Robert Heller writes on the subject of guard bits on the A real problem page.


Larry Smith - some special syntax to invoke expr - () would be best. This would make code much easier to read and more compact.It would be fairly easy to turn the feature on and off for backward compatibility.

This is not in the Tcl Way. If I (FW) had it my way, the correct thing to do for such a radical addition in that area would be to add math procedures to eliminate the need to use expr, for those who prefer a more purely Polish syntax.

LV In other words, FW, you would prefer to say something like

        set a [add 2 3 [divide [multiply 4 5 ] 7]]

YUCK!

FW: Well, I'm not sure what the command names would be, perhaps they would just be named after the math operators, as in GPS's suggestion in wish #34.

LV I still respond YUCK - I find reverse polish notation difficult to read and more difficult to code. I prefer the notation currently used by expr.

escargo 4 Jan 2006 - Just for the record, I must point out that this is Parsing Polish notation not rpn.


Lars H: Another possibility is to extend $ substitution. I have seen references that it was originally introduced as a shorthand for [set ...], and in view of this, it would be natural to consider making a form of it which is a shorthand for [expr {...}]. How about

set res $={2*$a + 3*rand()}

as a shorthand for

set res [expr {2*$a + 3*rand()}]

i.e., when $ is followed by = (a case not explicitly mentioned in the Tcl syntax rules, but in which currently no substitution will take place) and something in braces then these braces contain an expr-expression rather than the name of a variable. What one should perhaps think hard about here is the possibility to get rid of the need to put $'s before variable names in these $=-expressions. Simplicity and compatibility with expr suggest that they should be required, but forgetting the $ in expressions is probably the most common typo I make. This could be a sign that they are unnatural.


The tcl parser is a simple one - trying to replace expr ... with $= seems to me to be asking to break the parser for minimal benefit.

As for a new command to which one need only supply variable names, and not $, that too violates the tcl parser's rules - to get a reference to a variable, use the name only, but to use the value of a variable, precede the name with a $. It doesn't seem to me that the resulting command is going to be all that much beneficial, and on the contrary, it seems like it is going to make tcl slower, more complicated, and more difficult to teach and to use.


In bash one can write things like

$(($i+1))

for what would be

[expr {$i+1}]

in Tcl. The former currently refers to an element in the empty name array in Tcl, but if wish #39 (the empty string is no longer the name of a variable) was to be implemented, then the bash syntax (with double or single parentheses) could be used as an expr shorthand in Tcl. (In fact one could take the implementation of this syntax in Tcl 8 as an exercise in trace hacking. Set up a read trace on the empty name variable, and pass every index through expr to compute the array element value. Should work, but there would be a ridiculous amount of shimmering.) However, I think $={...} would work better syntactically than $((...)) or $(...). The best case for the latter two is the analogy with bash.

Larry Smith $(...) is not bad at all. "$(" is not currently defined so it is backward-compatible, and the () notation is natural for arithmetic expressions. It also plays well with the idea of $[] to expand the results of a command into a list. It even suggests that ${} is a cleaner and more logical way to indicate the "expand w/o eval" that we currently use "" for. This has always felt wrong because "" is otherwise meaningless in Tcl, and is frequently ignored or interpreted oddly (as in "10" == 10 in expr).

ulis Sorry, "$(" *IS* currently defined (empty named array):

set (1+2) 4
puts $(1+2)
->4

Setok -- Somehow that looks perlish. I prefer using the command: +, -, *, / etc. Result in lines like set a [+ $b $c $d]. Indeed, I use this form all the time.

Lars H: Whether it looks perlish is completely irrelevant (even though it is a classical killer argument in Tcl discussions; $ substitution got that critique too, in the beginning). What matters is whether is would be perlish, and I don't think it is.

As for having the math operators as explicit procedures, I think it would be a good idea to add a package for this to tcllib ASAP.


Sarnold In my little experience of Perl, I was surprised I could quite not force the interpreter to handle my numbers as integers. (they were automatically casted to float)

IMHO it is quite better to have both the casting operators, and the math operators as commands. I hope those changes won't make things harder to understand. IMHO it is expected from a developer to have skills with floating-point numbers and integers. Many people don't think so, but it is awfully too often required to know the limits of our machine-related number representations.


LV anyone who wants to write such a package, and submit it to tcllib, could probably get it in pretty quickly. And releases occur frequently enough that it would end up in the library distribution in a small number of months.


There is no compatibility problem, and if people like them then they could get byte-coded by Tcl 8.5. And it would still be possible to provide another shorthand like $= in Tcl 9, if some such suggestion is approved of.

Setok Well my point was not that being like perl is always necessary bad, but I find $={$a + $b} a bit strange to read, although now that I'm not so tired as when first reading this page it's not as impossible as I reckoned. As you mentioned opers-as-procs is a good compromise and I never liked expr having its own functions anyway (a [rand] procedure would be much more natural than rand()).


As long as expr continues to work, other syntactical sugar notations are easy enough to add on. I would not like to see such things become a part of the tcl.tar.gz distribution. However, if they were part of tcllib, that would be fine.

In ksh one could write things like

$(($i+1))

or

let $i = $i + 1

for what would be

[expr {$i+1}]

or

incr $i

in Tcl.

Lars H: I hope anonymous at 134.243.40.138 will refrain from making non-editorial changes to signed material in the future.


Martin Lemburg 17.02.2003:

if there is a possibility to mark expression with a special quotation to allow the bytecompiler to recognize expression, than it would easier a lot the writing of calculations.

What's about:

set pi '4 * atan(1)';

And ... even if it's not tcl-ish to do, but if we break tcl style by using "()" for functions, why not applying the nice patch for expr to allow every proc and command to be invoked in the same way and to access variables without the "$"-sign?

Why not ...

proc pi {} [expr {4*atan(1)}];
proc perimeter {r} {expr {2 * r * pi()}};

or

proc pi {} '4*atan(1)';
proc perimeter {r} {'2 * r * pi()'};

Lars H: The "special quotation" is probably too radical to meet general approval -- cf. the hassle with {*} (Tcl9 wish #3) -- but you're on to something concerning access to variables in expressions without using $. That would be very nice!

AJD Don't personally see any improvement in the above forms over the simple use of "$pi" or "[pi]". However, the use of argumentless functions within expr is something I've hacked around with... in particular, the case above of [expr { pi() }] to denote a constant. The core doesn't currently have any such beasts but they can be defined using the API function Tcl_CreateMathFunc. The idea being the core could define PI and other 'well known constants', saving users having to do it (possibly in a variety of places). There's even the opportunity for a teensy weensy performance gains (e.g., inline "2*pi()"). Maybe the ability to define "expr constants" could be exported to the script level... Overall though, probably doesn't buy enough beer to get taken to the bar.

Maurice.Diamantini (03 mars 2003): I'd like to see the functionality from vexpr or the vkit (is it dead ?) to be integrate into the expr command.

so that one could write

set a [list 1.0 4.0 3.0]
set b [list 5.5 6.6 9.8]
set c [expr {$a*$b}]

Another amelioration I'd like in tcl9 is to extend incr, and add a few other command like mult to work with other type than int (double or list ) (il c ist the liste above:

incr c 1    ;# add 1 to every element of the list c
incr c 3.1  ;# add a double
incr c $b   ;# add another list

RJM Picking up the suggestion above to use simple () parentheses: Why not doing it without preceding $? Just as "[" marks the start of a recursive script, "(" could mark the start of a math expression.

(simple example)

set y ($x*$a + 3.5)

Note that the shortcut should only apply to expressions that can be evaluated in an expr {...} command notation. In the case the expression cannot be evaluated with {} quoting, the conventional expr command should still be applied: This is the case where STRINGS must be evaluated as a math expression. In other interpreter languages it is also usual to provide a command for such cases.

Lars H: It would certainly be a language that made sense. The big problem with making ( behave like [ (but for expressions rather than scripts) is however that it makes another character special, and thus breaks every old script in which that character wasn't protected. In particular the syntax for array elements would get into serious trouble. I suspect the change would be too great for any Tcl version increment. Math operations as explicit procedures are probably the way to go.

Larry Smith: Actually, the more I look at arrays the more I think making them part of the language was a bad idea. An array should be accessed the same way other data structures are, with normal tcl syntax:

array set foo $bar "a string"

and

set x [ array set foo $bar ]

or

set x [ array get foo $bar ]

It really has no call for special syntax. It would also allow people to overload the array procs to add capabilities we don't have, but frequently want:

array set foo $x $y $element

would permit "real" numerically-addressed arrays to use the same keywords (this example keys off of the number of args, but it could also look at the values of the passed args to decide if this is a numerically-addressed array or a hash table.

RJM: I revised my contribution above. "[" may also appear without space as to execute a script. However it does not mean that "(" as an expression shortcut should also evaluate an expression when it appears without a leading space. I suppose expression results mostly happen to appear as command arguments. When it happens to appear inside a string, the shortcut shall not apply. In such cases, the expr command should still be used. When a variable with trailing space exists, there will be a problem. But I hardly believe that anyone would define variables with trailing spaces. Hence, backward compatibility shouldn't be a problem. Moreover: To my opinion, backward compatibility is not a holy cow. It is consequent backward compatibility that made MS-windows a monster. Consider also that scripts can easily be adapted to conform a newer Tcl version, using a script.

Another idea (yep, this is a brainstorm page): When "(" is not acceptable, a new command "=" should. For evaluation purposes, enter the following proc:

proc = {args} {expr $args}

According to my previously stated condition, this command should only work with true expressions (i.e. be equivalent to expr {text}). This way, any [= 5*1.2 + $a] inside a script text would execute the fast way round. Oops! (note few weeks later) This intention violates with Tcl rules - Tcl would do substitutions before passing the argument to the "="-command. So the fast way round is never possible with a new command. Conclusion: only a new substitution type will do (see above: $={...}, '...', $(...) and (...) ). Given the complaints with "(" I vote for $(...). Uhh, this is empty array access (see above)

Recalling backward compatibility issues, I'd recommend a little bit more courage in changing things in Tcl. expr is historically given, because everything was a string in Tcl. When no shortcut will be introduced, I believe Tcl will early or late fade out in computing history, simply because the choice in scripting languages is really large. Anybody who is selecting a scripting language will tend to not select Tcl when he/she encounters "set a [expr {....}]". This is Tcl Marketing-relevant!


RS: You can save a level in call stack by using this instead:

interp alias {} = {} expr

(RJM: still for evaluation purpose: executes slower and does not include {} in expr).


DKF: The problem with special syntax for expressions (or at least special syntax that isn't standard command invokation or the = alias) is that this is a major departure from the existing rules; you'd be making some kinds of syntax special, and one of Tcl's key strengths is that there are no special global syntaxes. The expr syntax is always just a local syntax.

A few notes here...

  • Unbraced expr was special in the alphas of Tcl 8.0, but this was deeply unpopular with many very influential Tclers.
  • It is possible to get commands that are as efficient as the expr bytecode, or rather it would be if it wasn't for the fact that we're not very proud of the actual compiler API and so haven't exposed it publically. :^)

slebetman 15 Mar 2006: By unbraced exprs do you mean [expr $foo + $bar]? Why was special treatment of unbraced expr unpopular? Was the C code ugly? To me braced exprs are unpopular to the extent that I'm often willing to sacrifice speed rather than break my old habbit. If special treatment of unbraced exprs were already coded can't we just put it back in so that we don't need the braces to bytecompile exprs. That will also make things like let faster since currently the expr that it depends on is not bytecompiled.


RJM It can be seen, that no practicable shortcut for expr {...} seems to exist. Therefore, another approach should be discussed. Why not let appropriate commands expect an expression in one or more of its arguments, like the if command does. For example coords arguments for canvas operations. They must always be numeric, so rewriting commands involving coords to expect expressions would allow us to embed calculations within such commands in a more compact fashion, which improves readability. I also think this would not affect backwards compatibility problems, since numbers are already "expressions". Further, existing code, where [expr {....}] is already embedded, also yields no problems. I would see this idea closely related to what is written in What some command arguments expect. If the idea is interesting, an inventarisation of commands with this respect should be undertaken. Further, some new commands could be defined, such as xset/nset for expression/numeric set etc.

KPV See tip #176 [L2 ]. Personally, I like what that tip proposes--it would clean up most of the cases where adding expr gets really awkward. But I've not heard much momentum behind it.


Lars H: Another TIP which should be mentioned on this page is #174 [L3 ]. It is pretty much what is listed as Wish #34 above.

HGO: And yet another TIP which should be mentioned on this page is #232 [L4 ]. It may help in many of the above stated cases.


AMG: What about $[...] being shorthand for [expr {...}]?

I saw mention of $[...] above, but if I read correctly it was in regards to a feature already provided by {*}[...], so this character sequence been freed up. Does anyone currently use this sequence? At the moment it means: eval the script "..." and prepend the result with $.

We'd have to change the parser to quote between $[ and ], and it would have to handle nesting correctly. Important inconsistency: we do not do this for $(...)!

07nov05 jcw - This is not necessarily an inconsistency. [...] gets parsed as usual, the $ in front of it takes the inside (say $x) and replaces it with [list expr [list $x]]] - IOW, "$" before a [ gets interpreted as "expr" after the [ (more or less, the arg also needs to be treated as surrounded by {}'s).

AMG, 9 Dec 2005: That's not quite right... expr [list $x] and expr {$x} aren't equivalent. Surely you mean the latter.

The inconsistency I refer to is $[...] having implicit {}'s but not $(...), and you haven't addressed this. Maybe I just don't understand what you're trying to say. Care to elaborate?

Question: Should $[...] be recognized by the [expr] parser as well as the Tcl parser? I imagine $[...] inside an [expr] expression would have precisely the same effect as (...).

Larry Smith How about this idea, then:

interp shorthand <srcproc> startchar endchar

This would allow creation of application-specific shorthand expressions. For ordinary tcl, one might declare:

interp shorthand expr ( )

Thereafter the interpreter scanner would spot (...) and turn it into [ expr ... ].

This also allows people with full unicode editing to use things like 〚...〛, ⌊...⌋ or even <...> as shorthand for various options, which could include things like constant constructors, object calls, regexp sugar, whatever.

Lars H: The problem with this is that is changes Tcl syntax in rather unpredictable ways. list-quoting is one issue -- if list is to be usable as a generic command constructor (which is very established in Tcl) then defining "shorthands" like this requires changing the canonical representations of lists to ensure all shorthands to be quoted! That quickly becomes very messy.

Larry Smith Actually the above was rather tongue-in-cheek (and I was amazed by the variety after a very cursory look through a unicode font), but truthfully, this doesn't change tcl's syntax except to provide semantic meanings for new symbols in a context that right now only permits {...} and [...]. The symbols listed would mean nothing if they were inside pairs of the existing delimiters.

Lars H: (Sigh.) Of course they wouldn't be subjected to substitution within {} (however, they would have to be subjected to substitution inside brackets, as that is just another command -- think your syntax suggestions through before you post them!), but that's no help to you. Tcl does not have any context that "only permits {...} and [...]". "Barewords", as they are sometimes called, are legal everywhere and the string representations for lists uses them not only for very simple elements without whitespace, but in fact relies on them for the trickiest cases.

Larry Smith I do think my syntax suggestions through before posting them. Just because you don't like them is no excuse for such a remark.

As far as tcl syntax and parsing go, the scanner is the one place in the interpreter where "", [] or {} have meaning. Providing new delimiters simply converts an extremely unlikely string containing such delimiters into something tcl recognizes and executes.

 set x 〚foo〛

...under the current system sets x to 〚foo〛, true enough - but that is an extremely unlikely sort of string. Giving it a semantic - that is, extending the scanner to recognize ĺ - only breaks a very unlikely case. Frankly, if you are coding that way, you deserve to have your code broken.

Lars H: Well, a rereading with your reply in mind indicates that what I took for a lack of thought may simply have been an unclear re-presentation of the exact same ideas as that you started out with, but as it stands it really sounds as though you introduce the extra principle that "expr substitution" should not be triggered between brackets, which would render it of limited utility. Also note that brackets and braces don't have anything in common syntactically; there is no "one place" in which both are handled and (unlike brackets) braces are only special at the beginning of a word.

Regarding your argumentation:

  1. I personally don't write things like "set x 〚foo〛" right now,
  2. but people who do don't deserve to have their code broken.
  3. Indeed, the argument that this "is an extremely unlikely sort of string" is the syntax analogue of the classical argument that the government should be allowed to violate human rights: "We probably won't do it to anyone you know (and in the unlikely event that anything bad ever happens to anyone because of this then -- as is evident from us even invoking this extraordinary law -- these people obviously must have been evildoers anyway)."
  4. None of this has even begun to address the problem of list-quoting that I raised. If string representations are not changed to quote new shorthands, then you open up for attackers getting malicious code evaluated. If string representations are changed, then you have to make them interpreter-relative, but Tcl_Objs don't belong to a particular interpreter!

Sure, if you implement a Tcl/2 then you can make its syntax mutable to your heart's content, but doing so is far from free from side-effects, and you do not seem to appreciate that.


Lars H: My present (2006) opinion is that shorthands in general are unTclish. What should be done instead is to create more capable little languages, in which syntax rules could be more algebraic. Rather than

set r <hypot($x,$y)+1>
set x1 <$x/$r>
set y1 <$y/$r>

where there is constant switching between Tcl and expr, one should do also the variable assignments in some expr-like language, e.g.

calculate {
   r = hypot(x,y)+1;
   x1 = x/r;
   y1 = y/r;
}

Larry Smith I like the idea, but the real problem here is the messy syntax that arises from the need to calculate expressions being provided to other commands. List extraction, for example, where the first index is given (j) and the end is computed: (j+5). The innocuous-looking (j+5) balloons into [ expr { $j + 5 } ] (not forgetting the need to curly brace arguments to expr).

Lars H: TIP#176 has already adressed that problem in the case of indices. For other small calculations TIP#174 provides a nice solution. That only leaves the high end of expr usage to deal with!

Larry Smith #176 works but it does put the onus on the command to provide such functionality. #174 is not, IMHO, a nice solution. It is at best a slightly less ugly one.

In all honesty this is a very restricted domain, and becomes even more so if commands can accept simple expressions like $j+1 or end-1, but it's a wart, and it adds complexity to commands to support this. General access to simple computations is really needed, but I'm begining to think expr is over-specified. Expr also supports floating point, trancendental functions and lots of other stuff not really needed to solve the problem we really are trying to deal with here.

<sarcasm> Now that we've broken down and added {*} maybe we want to extend the idea to include {compute} - that is, expand and expr in context - where {compute}j+1 is read as [ expr { $j+1 } ]. </sarcasm>

In any case, expr is really starting to rub me the wrong way. As a means of computing indices or other such trivia, it is way over-specified. As a means of doing advanced math, it's way under-specified. As a means of doing other types of computation problems, such as matrices, vectors, or what-have-you, it fails utterly, yet it remains the repository of the logic needed to build upon and so insinuates itself into every solution, and again our code grows cluttered with [expr {...}].

I'm thinking more and more fondly of Tcl/2. And even more after this last exchange.

AM (17 march 2006) Actually a procedure like [calculate] has already been written - A little math language revisited

Gosh, it would be nice if the mechanism of expr was expanded to vectors and matrices and what not ... I have contemplated and started several experiments in that direction. All the ingredients are there. Now it is a matter of concocting the right receipe and bake the penultimate script. Main problem: time.

Sarnold (7 sept 2006) I am currently experimenting a syntax

{math}{...} 

to be handled by a modified Jim interpreter. What I expect is RPN generated at parse-time from expr-like syntax. This means operators and math functions should be available as commands at Tcl-level.

Example:

{math}{$i + 2 > 0}  -> [mathfunc.gt [+ $i 2]]      (gt = greater than, second arg defaults to 0)
{math}{$j && $i>0}  -> [and {$j} {mathfunc.gt $i}]

(and is a command, but to allow lazy evaluation, it needs braces around its args)

But I face a huge problem : I still need the expr parser because, if I wanted to bash it, I had to write {math} before each expression, including if, while, for, etc. Though, there is an advantage to this approach: you CAN redefine math operators to handle vectors, matrices, bignums, everything you need... (because +, -, *, / are commands in Jim) But don't forget it is *experimental* and does not concern Tcl but a little bit.

CMcC Mon 11th Sept 2006 sees no need for incorporating infix notation in tcl's global syntax - it's a major global change for not much local virtue and significant global cost (IMHO), I refer you to little language for a growing collection of the places in tcl where specialised parsing is contained within a linguistic {}-scope.

However, I would like to see expr syntax expanded to permit #-comments, because it would be nice to be able to comment complex if conditionals.

NEM: I actually quite like the idea of () as syntax for expr, but it would have to be done in the same way as {} and "" -- i.e., ( is only special at the start of a word. This would rule out any clash with array notation, and also with parens in ""-quoted strings (which are rife). The resulting syntax seems quite elegant for the common cases:

lindex $xs ($ptr/2+1)
set res ($expr + [mycommand ($subexpr)])

It doesn't really work for while or for, where the expression needs to be evaluated repeatedly (I'm assuming eager evaluation of expressions, i.e., (...) is equiv to [expr {...}]), but I can live with those as they are already concise except for the nested sub-expression case, which this proposal also nicely handles:

for {set i 0} {$i < [lindex $foo ($x+1)]} {incr i} { ... }

You could clean up if by making it accept a plain boolean value rather than an expression. That would result in syntax like the following:

if ($a < 12) { ... }
if [mycmd ...] { ... }

Which both seem nicer to my eye than the current braced varieties.

Justification for adding a new global syntax? Well, firstly, I think expr is probably important enough to warrant it. Use of mathematical expressions is extremely common. If I was looking for parts of the language that justified syntax, expr would probably come after $-syntax for variables, but before either array syntax or {*}. I also think it makes the language more newbie-friendly, as explaining expr and about always bracing expressions is perhaps less than inspiring. Finally, most of the alternatives (if you believe there is a problem to be addressed at all) seem to involve adding features to the expr little language, until it is no longer a little language, but actually quite a large one. With comments, assignment etc, the emphasis seems to be on including more and more of your source code inside expr -- the logical conclusion would be that eventually all source code is in expr syntax and Tcl becomes the little embedded language! I'd prefer to keep expr minimal and just give it some sugar.


RS: Note however that the (...) syntax isn't free for the taker any more - it rather refers to the array named "":

% set (x+1) 5
5
% puts $(x+1)
5
% parray ""
(x+1) = 5

At least Stooop (for which this feature was introduced) would break if (...) received a different meaning.

NEM: Note that $(...) could continue to work. Only [set (...) ...] would break. You could use [array set] to work around that (perhaps aliases to [self]?), but it would definitely require a change to Stooop. I think it would be worth it -- empty array names are cute, but little used.


CMcC (Wed 13th Sept 2006) Discussion on tkchat suggests there's a problem with some aspects of expr being made into global syntax: specifically, &&, || and ?: operators explicitly don't evaluate their second argument in some cases. This is a major departure from the tcl evaluation/substitution model.

RS: On the other hand, you sometimes need such short-circuiting - consider these not atypical cases:

if {$d != 0 && ($n/$d) > 1} ...
if {$d == 0 || ($n/$d) > 1} ...
expr {$d==0? 0: $n/$d}

NEM: I don't understand. As I said, a global syntax for expr would be precisely equivalent to [expr { ... }]. Short-circuit operators would continue to work exactly as they do now in braced expressions. This is an argument against both maths operations as commands, and against changing Tcl_GetIntFromObj et al to recognise some cut-down expr syntax.


Robert Joy 2006-09-13 6:32am PST: While I respect that one might want to drive expr into a more common paradigm. I personally don't see a need to move in that direction. In some ways I prefer the use of expr as it it becomes abundantly clear that one is defining a mathmatical expression and not just creating a string that looks like an expression. For the sacrifice of a few extra characters to change to a new paradigm it's just not worth all the effort. Just my 2 cents for what it's worth and I certainly don't mean to upset anyone but I think those who originally created expr had the right idea.


WHD: For some time now, I've been using a helper command called "let". The two following statements are equivalent:

set a [expr {$a + $b*$b + $c*$c}]
 
let a {$a + $b*$b + $c*$c}

This doesn't help when you're using an expression in-line, as an argument to another command....but it makes complex computations much easier to read. - RS: Something like this?

proc let {_var expr} {
  upvar 1 $_var var
  set var [uplevel 1 [list expr $expr]]
}

CGM 2008-06-11: Another suggestion - use { .... }= as shorthand for expr { .... } . This is both compact and backward-compatible to the same degree as {*}, since it's currently an error "extra characters after close-brace". Using {} braces preserves the quoting semantics to avoid double-substitution of variables. There's an ambiguity over {*}= but the expand meaning should precedence here as expr {*} is an error anyway.


JMN speaking of {*} .. it seems to me that it would now be natural to use another such prefix for expr.. e.g {#}{1 + 2}

Looking above I see Sarnold has already suggested something like this with {math}{...}

JMN 2008-06-14

While at first glance this mightn't seem much better than expr {...}, I think that in the vast majority of cases the braces surrounding the actual expression could be left off simply by formatting it without whitespace. E.g.,

{#}1+2

If a tcl command with whitespace is included in the expression, you still wouldn't need braces.

 {#}[llength $somelist]/2

tcltkd : 2008-06-12: A classic technique:

proc unknown {args} {uplevel 1 eval expr $args}

Is it dangerous? If is,some global interpreter flag should be employed.

NEM There's too many evals in there, so it is dangerous. A more correct version would be:

proc unknown args { uplevel 1 [list expr $args] }

But note that it has problems with some expressions, such as && and ||:

% [set a 1] || [set a 2]
1
% set a
2

Versus using expr directly:

% expr {[set a 1] || [set a 2]}
1
% set a
1

NEM A possible approach is to write a pre-processing version of proc that implements syntax for expr:

proc func {name params body} {
    regsub -all {([^\\])\(} $body "\\1\[expr {" body
    regsub -all {([^\\])\)} $body "\\1}\]" body
    uplevel 1 [list proc $name $params $body]
}
func foo {x y} { return (1+[lindex $x ($y*4)]) }

It is a bit too eager on substitutions, requiring backslash-escaping to include a literal parenthesis anywhere, and not respecting braces. Still, it does allow a glimpse at a remarkable reimagining of Tcl:

func if {cond then {_else_ ""} args} {
    set args ([llength $args]==1 ? [lindex $args 0] : $args)
    uplevel 1 ($cond ? $then : $args)
}
func foo a {
    if ($a < 0) {
        puts Negative
    } else if ($a > 10) {
        puts Large
    } else {
        puts Small
    }
}

FM I like the concept of little language. But we could need to obtain the value of each calculation, so we should return a list. If we write :

set L [math {
   x=0;
   y=1;
   z=$y+1
}]]

we should get :

lindex $L 0 --> 0 (x value)
lindex $L 1 --> 1 (y value)
lindex $L 2 --> 2 (z value)

Then, we should be able to write in tk for instance :

wm minsize . {*}[math {[winfo screenwidth]/2];[winfo screenheight]/2}]
wm geom . [join [math {[winfo screenwidth]/2];[winfo screenheight]/2}] x]

instead of :

wm minsize . [expr {[winfo screenwidth]/2]}] [expr {[winfo screenheight]/2}]
wm geom . [expr {[winfo screenwidth]/2}]x[expr {[winfo screenheight]/2}]

or somethings like

[canvas .c] create line {*}[set L [list]; for {set i -50} {i<=50} {incr i} { lappend L {*}[math {x=0.1*$i; y=$x**2}] }; set L]

Lars H: When only one result is wanted, it would be quite a nuisance to have to lindex it out of a list of results. The approach I took in infix was to introduce , as a list-construction operation — this way one can get the effect you ask for by doing

set L [infix {} {
   x=0;
   y=1;
   z=y+1;
   x,y,z
}]]

or

set L [infix {} {
   x=0,
   y=1,
   z=y+1
}]]

FM: I found it more clear to return a list. But maybe it should be another command which "list expressions"

proc lexpr {expr} {
    namespace path {::tcl::mathfunc ::tcl::mathop}
    set L [list]
    foreach ::EXPR [split [string trim $expr] "\n\;"] {
        if { ! [expr {$::EXPR eq "" || $::EXPR eq {\{} || $::EXPR eq {\}} }] } {
            lappend L  [uplevel {
                namespace path {::tcl::mathfunc ::tcl::mathop}
                expr [subst {$::EXPR}]
            }]
        }
    }
    set L
}

Test :

set Central [dict create {*}[lexpr {
    "Rayon"; [set r 3]
    "Perimeter"; 2*[set pi [acos -1]]*$r
    "Area"; [set A [* $pi [** $r 2]]]
    "Solid Angle"; 4*$A
    "Volume"; 4*$A*$r/3
}]]

I know that you've made a lot of works on these proc infix. But I think that implementing a little langage by this way make us loosing tcl inside. Will we need to develop a new parser ?

Lars H: My point was only that with a list construction operation, you can return a list as easily as a single value. How the little language supporting that operation is implemented is a separate issue.

Let's think about Tcl. On one hand Tcl is verbose, but on the other hand programs are tiny. Why ? It's seems to me thats it's because commands could easily communicate together. I come to LOP, we could have a whole set of little langage but the problem is to be able to make them communicate together. In tcl we could write things like this :

expr {[
       set x 1
       if {$x == 1} {
           set y [expr {[info frame]*cos($x)}]
       }
       set z [expr {$y+$x+[llength [info proc ::*]]}]
       list [info level] $x $y $z [winfo children .]
      ]}

This is why Tcl is wonderfull. In fact, with the introspection facility of tcl, I think it would be possible for all this command to "know" they are in the expr context. Let push further this idea. I could implement this only this with lexpr because "info frame" tells only which proc is the caller (but not a command like expr !). Before let's do an little proc as "set" :

proc = args {
    set dico [dict create {*}[info frame -1]];
    if {[dict exist $dico proc] && [dict get $dico proc] eq "::lexpr"} {
        uplevel "set [lindex $args 0] \[expr {[lrange $args 1 end]}\]"
    } else {
        uplevel "set [lindex $args 0] {[lrange $args 1 end]}"
    }
}

This proc is looking upframe, test if it is call by lexpr (the better would be by expr) and adapt himself. let's do some trivial test :

lexpr {
    [= x 1]
    [= y 2]
    [= z $x + $y]
}
-> 1 2 3

= z $x + $y
-> 1 + 2

The proc "=" adapt himself to the caller. Imagine this kind of things could be implement with set and other command like list, so that they detect an expr context and apply expr to their argument. We could write things like this :

set Central [dict create {*}[expr {[
                                    set pi (acos(1))
                                    set r 3
                                    set P (2*$pi*$r)
                                    set A ($pi*$r**2)
                                    list Rayon $r Perimeter $P Area $A {Solid Angle} (4*$A) Volume (4*$A*$r/3)
                                   ]}]]

Another way to achieve this is to use the proc know in a modified version of expr

namespace eval ::lexpr {
    proc know {cond body} {
        proc ::lexpr::unknown {args} [string map [list @c@ $cond @b@ $body] {
            if {![catch {expr {@c@}} res] && $res} {
                return [eval {@b@}]
            }
        }][info body unknown]
    }

    know {[expr $args] || 1} {expr $args}

    proc let args {
        eval eval [list expr $args]
    }
    namespace unknown ::lexpr::unknown
    namespace ensemble create -map {let ::lexpr::let}
}

Test :

lexpr let {[
     set pi [acos(-1)]
     set r 3
     list Rayon $r Perimeter [2*$pi*$r] Area [set A [$pi*$r**2]] \
          {Solid Angle} [4*$A] Volume [4*$pi*$r**3/3]
]}

In this way, propagating the context, Tcl could approach a contextual grammar (here in 15 lines of code) and all Tcl commands remain inside !

Maybe a flag could be send between TclParseExpr and TclParseCommand to keep the knowledge of the context ? So, called from TclParseExpr, TclParsecommand would first try to parse a script inside it as an expression and in case of error, parse it as usuel. This could be a new concept in tcl, the ability for the parser to "know" in which context it is, and so it could be implemented for other commands. In those cases (only for expressions in a first step), {[ ... ]} mean propagate the context.

Lars H: All that is really an orthogonal idea (not tied to expr, although it can be applied to it), but anyhow… As I see it, the main problem with this kind of "adapt behaviour to context" solutions is that they don't scale at all well as the complexity (number of possible contexts) of the system grows — there are simply so many things that have to know about each other to make it all work, that it effectively becomes unextendable.

It is often possible to achieve much of the intended benefits by using a namespace as "context", i.e., having different behaviours of a command really be different commands. Besides already being supported in the language, this also scales well, because it is not based on having the computer guess (is this a command or expression or ...?) what you meant by what you wrote! It doesn't support every syntax one might imagine, but it very often supports a syntax that is as compact and expressive as the one that might be found in other languages.

FM So you mean that the better way should be to give the hability to the programmer to implement context sensitive procedure rather than put it in the core, because the extrem complexity of the approch ? You certainly right. Additional tests to add would be a lost of time. In fact, tcl'ers don't have so many way to explore this complexity, and so to see how it is extrem.[ ] substitution is not context sensitive, substitutions have no "color", since it occure before the invokation of the command prefix of a line. To experiment such a behavior, I would like to write something like :

expr 4+[Tclproc $a+3]*5

... and be able to know I'm in the expr context, so I can apply expr to the arguments of TclProc. But, as far as I know, neither info frame, nor info level can give me the information about the first word of the command line beeing evaluated, but only the first word of the nested script currently evaluated. It should possible since the parser has seen the command prefix of the line before recursively evaluate the nested script. Maybe NRE could offer such an opportunity. Suppose we have a command : info lineprefixe

rename set ::_set
proc set {var val} {
    if {[info lineprefix] eq "expr"} {
        uplevel ::_set $var [expr $val]
    } else {
        uplevel ::_set $var $val
    }
}

expr {[set B 4+[set A 3+3]*5]}

set B [expr {4+[set A [expr {3+3}]*5}]

rename list ::_list

proc list args {
    if {[info lineprefix] eq "expr"} {
        foreach e $args {
            lappend L [expr {$args}]
        }
        return $L
    } else {
        return [::_list $args]
    }
}

expr {[set L [list 1+1 2+2 3+3 4+4 5+5]]}
set L [list [expr {1+1}] [expr {2+2}] [expr {3+3}] [expr {4+4}]]

This is limited to the cases where expr is the first word of the line, and equaly when all arguments of the nested command are to be evaluated as arithmetics expressions. There is also a (big)problem since all those procs above (set, list) become slower. Other ideas should be found

if I write at the beginning of a line

y=sin($x)/$x

I get an error. Of course y=... is not a command ! I could write that y is a proc

proc y {= args} {
    uplevel set y [expr {$args}]
}

But I'm obliged to write

y = sin($x)/$x

But if I write

y= sin($x)/$x
y =sin($x)/$x

it's return an error. So, I'm obliged to handle all this case (splitting, ...) with many procs and to define this procs for each variables ! I could use the unknow handler, but as it happens after everything has fail, it is too slow for arithmetic, and of course, won't work if y is a command. A solution I see, is a new interp command which would glob the command prefix and apply the good transformation to it.

interp match path <glob pattern> path commandTransformation

# detect a command prefix glob pattern and return the command to be executed
interp match {} *=* {} apply {{exp args} {lassign [split $exp =] var e; return {set var [expr $e $args]}}}

X=2*3+cosh(3)-5

A=[B=[winfo width .] > [C=[winfo screenwidth]] ? $C : $B]

The limitation is that all the command is affected. What if we want only a few arguments to be expr(essed)? Another idea, it is well know that :

{something}$List

always throw an error (except for {*}, the expansion prefix). An error in the parser means that we can extend the parser semantic in a backwarded compatibility way. The place has be done in the parser to handle such case. So it should be extend. A wonderfull way should be to let the programmer extend it by himself !

interp elementprefix path <elementprefix> path command args

interp elementprefix {} = {} apply {{args} {foreach e $args {lappend L [expr $e]}; return $L}}

lassign {=}[list 1+1 2+2 3+3 4+4] A B C D
lassign [list {=}1+1 2+2 {=}3+3 4+4] A B C D

wm geometry . [join {=}[list [winfo width]/2 [winfo heigth]/2] x]

interp elementprefix {} mid {} apply {{args} {expr double([join $args +])/[llength $args]}}
set M {mid}$L

See also infix, xbody.


This page show an interesting discusion about this topic: [L5 ]


Twylite 2012-12-14: Discussion on tcl-core in the thread "Expr evolution" (tcl-core archive: [L6 ], [L7 ]) has included thoughts about a shorthand syntax for expr.

This section presents a summary of the parts of this page and the tcl-core discussion that relate to a new global syntax for expressions (to be supported by the Tcl parser in Tcl 9).

To clarify: the area of interest (in this section) is specifically a global syntax that will be precisely equivalent to expr {...}. The syntax will be recognised by the Tcl parser (which will involve a change to the Dodekalogue) and evaluated as an expression by the same internal mechanism as expr. No changes or enhancements to expr itself are contemplated.

Justifications for a global syntax

  1. Code correctness (protection against double substitution): unbraced use of expr is a source of bugs and security holes; for example expr 1 + $a may execute code in $a (try set a {puts "gotcha"}). Unbraced use is nontheless common (due to laziness and lack of understanding of the risks). A more terse (read: lazier) syntax that doesn't permit double-substitution will lead to more correct code. To avoid double-substitution we have to introduce new syntax. Even an expr variant that accepts a single argument (rather than concatenating a list of args) could be called as expr 1+$a rather than expr {1+$a}, leading to double-substitution.
  2. Terseness and prettiness: a terse syntax for expr is frequently requested on this wiki, on tcl-core and on c.l.t; this is clearly a popular desire. NEM (on this page) also cites mathematical expressions being common enough to warrant a global syntax, and that such a syntax will make the language more newbie-friendly. A terse syntax will combat the growth of expr into a less-little language.

Concerns

  1. Backwards compatibility: many of the global syntax proposals would reserve syntax that has existing meaning (in Tcl 8.5 & 8.6), potentially breaking existing code. Other constructs use previously-illegal syntax, but are less intuitive. Opinions vary on whether or not to break compatibility, and how widespread the impact of a break would be for a particular syntax.
  2. Intuitive/ugly syntax: there is much debate around how intuitive or ugly each syntax proposal is. For newbie-friendliness the goal should be an intuitive syntax.
  3. Global syntax is unTclish: DKF and Lars H have expressed this view on this page. See Terseness above for a counter-argument.
  4. Global syntax should not restrict expr syntax: Lars H noted on tcl-core that the parsing rules of the global syntax - which would become part of the Dodekalogue, should not impose any limitations on the language of expr, as such limitations could prevent the evolution of the expr little language. I note that the effective limit is that the global syntax should be as expressive as the contents of a braced expression (matching what can be achieved using expr {...}). Ideas for extending expr that may influence the language syntax include: omit $'s from varnames in expr, support comments, support list/array/vector expresisons.
  5. Nesting in expr: should the global syntax be recognised by the expr parser as well? Currently one can nest expr by writing [expr { 1 + [expr { 2 + 3 }] - should a global syntax need to nest in the same way?

Non-solutions

  1. Salt and Sugar "Cheap sugar" proposes interp alias {} = {} expr (credited to Jeff Hobbs). CMcC makes a similar suggestion on tcl-core. RJM and I point out that this merely addresses terseness and doesn't fix double-substitution. The slightly safer proc = {exp} { expr $exp } makes double-substitution less likely but doesn't prevent it (e.g. = 1+$a).
  2. tcl::mathop For short expressions, e.g. a simple addition of two numbers, using the tcl::mathop commands can be every bit as terse as any expr shorthand suggested. Thus instead of [expr {$i+1}], write [+ $i 1].

Syntax proposals

I have collected syntax proposals from this page, other pages on the wiki, and tcl-core discussions:

ProposalSourceInterpretation (*)Works within string (**)Backwards compatibleParser impact (***)Comments
$={...}Lars H (this page)SubstitutionYesYes (maybe)Minimal
$=(...)Mistyping of $={...}SubstitutionYesYes (maybe)MajorTwylite 2012-12-14: Uncertainty over rules for identifying the end of the word; in particular the existing rules for array variables don't allow nested parenthesis so this proposal may break compatibility.
{=}{...}
{=}"..."
{=}[...]
Twylite on tcl-core, others?WordNoYesMinimalTwylite 2012-12-14: The definition must not permit a bareword after the {=}, ensuring that double-substitution must be intentional.
{...}=CGM (this page)WordNoYesMinor
{#}(...}JMN (this page)WordNoYesMinimalTwylite 2012-12-14: Discussion on {expand} suggests this syntax as a shorthand for llength
{math}{...}Sarnold (this page)WordNoYesMinimalTwylite 2012-12-14: Proposed more as a distint little language than a global syntax for expr
$[...]Larry Smith, jcwSubstitutionYesNoMinor?Twylite 2012-12-14: See also discussion in {expand}. Considered unlikely to break existing code.
$(...)Steve Bennett, othersSubstitutionYesNoMajorTwylite 2012-12-14: Added to Jim in 2010. Known conflict with the empty named array (used e.g. in Stooop)
$((...))This pageSubstitutionYesNoMinor to MajorTwylite 2012-12-14: From earlier on this page: Used in Bash and ksh; not strictly backwards compatible but unlikely to break existing code.
(...)NEM, othersWordNoNoMajorTwylite 2012-12-14: Known conflict with unquoted 'regexp (a|b|...)'. Uncertainty over rules for parsing/nesting and identifying the end of the word. NEM created a Jacl patch that implements this proposal (2009).
((...))Reinhard Max on tcl-coreWordNoNoMinor to MajorTwylite 2012-12-14: Similar to shell scripts; strictly backwards compatible but unlikely to break existing code.
'...'Martin Lemburg (this page)Word?NoNoMajorTwylite 2012-12-14: Earlier on this page Lars H calls this "too radical"
={...}xbody, TwyliteWordNoNoMinimal
Numbers as commandsFB on tcl-coreWordYesYesNoneA very simple proof of concept can be written using unknown
⟦ ... ⟧ FM Larry SmithSubstitutionYesYes (allmost)Minor ?
[(...)] FM Substitution yes yes (allmost) minimal location : on github . Nestable. the form would be array((expr)) onto variables. Use TCL_TOKEN_SUB_EXPR to mark the syntax, expr parser to check it, expr compiler to compile it, and Tcl_ExprObj to get a value when needed. No double substitution occurs. It's slightly faster (25 %) than the expr command, since it skips the eval step

(*) Interpretation: 'Word' means the syntax is only recognised as a whole word, so the start of the proposed syntax must match the start of a Tcl word (same rules as the expand operator {*}). 'Substitution' means the syntax can word in any substituted context, including in the middle of a string.

(**) Words within string: A (slightly redundant) indicator of whether you can put an expression (using the proposed shorthand) in the middle of a string. e.g. set a "value=$(1 + $a)" will evaluate '1 + $a', but `set a "value={=}{1 + $a}" will substitute $a into the string.

(***) Parser impact: This is an estimation of how invasive the change is to the parser implementation (and indirectly how difficult it is to specify the change in the Dodekalogue). A minimal change is expected to leverage the existing support for {*} and retarget how the word is processed. A minor change involves slightly more work, possibly elaborating on a corner case. A major change involves a new nested parsing rule, and dealing with how it interacts with substitution and other nested parsing rules.


FB - 2012-12-14 14:31:17

Numbers as commands

This proposal needs no change to the Dodekalogue, but instead relies on the existing power of Tcl. Under this proposal, mathematical expressions become regular Tcl commands with the same syntactic and substitution rules as the rest of Tcl, unlike the current expr command.

For this to work, the interpreter must recognize numbers as implicitly defined commands. The first numeric operand is the "virtual command" name, remaining operators and operands are passed as arguments to this "virtual command". So operators and operands are words separated by spaces, and grouping is done with square brackets as subexpressions are regular Tcl commands themselves. Math functions are defined as Tcl commands too. This infix math syntax naturally blends into Tcl, unlike expr variants defining their own sublanguage.

For example:

set x [1 + 2]
# -> 3
set y [2 * [3 + 4]]
# -> 14

set distance [sqrt [[$x * $x] + [$y * $y]]]
# -> 14.317821063276353

An interesting side effect is that numbers evaluate to themselves recursively, e.g:

[[[1]]]
# -> 1

Operators allowing lazy evaluation, such as the ternary operator ?:, shall accept braced scripts as operands:

0 ? {1 + 2} : {3 + 4}
# -> 7

This proposal can be tested under current Tcl versions with a very simple proof-of-concept unknown handler that redirects to the existing expr command (note: doesn't work with lazy evaluation):

rename unknown _original_unknown
proc unknown {cmd args} {
     if {[string is double $cmd]} {
         uplevel 1 [list expr "$cmd $args"]
     } else {
         uplevel 1 [list _original_unknown $cmd] $args
     }
}

# Math functions must also exist as Tcl commands
proc sqrt i {expr {sqrt($i)}}

FM why limit the shorthand to be a sequence of ascii caracters ? Is Tcl not unicode compatible ?

For instance : ⟦ (\u27e6) and ⟧ (\u27e7)

lassign ⟦list(1+1,2+2,3+3,4+4)⟧ A B C D

FM 2023/06/12 There is some new TIPs on the subject and a long discussion on the tcl-core mailing list now.

I agree that TCL 9.0 should improve the situation about mathematical computation. But everything in this concern is not only about the way to write computation more concisely. Maybe a more global and general approch is needed.

My thoughts about it : string is the interface between Human and Computer. But these strings need to folow some rules to be usable by the computer. They must be read through a syntax. A string has a meaning in a language.

Tcl has the power of a Turing Machine. All this power is about substitution. So, increase Tcl means increase his substitution power. This substitution shall depend on the context, and the way it substitutes depend on the language of this substitution, along its syntax.

A mathematical syntax imply a mathematical substitution. The mathematical syntax should be extend to allow assignement and multiline.

Tcl is looking at the future, not the past. ASCII is too limited.

I'm in favour to create a mathematical substitution with '⟦' and '⟧' (or, in ASCII, "[| " ... "|]") Ex :

   set d ⟦sqrt($x * $x, $y * $y)⟧

or (in ASCII)

   set d [| sqrt($x * $x, $y * $y) |]

Also, a mathematical mode could be interesting. Let's say, something ⟦*⟧{ ... list of expression} Let's compare an example taken on the Tcler's wiki :

      set x1Px [expr { round( ($PxPerUnit * $y1) + $xmidPx ) }]
      set y1Px [expr { round( $curCanHeightPx - (($PxPerUnit * $z1) + $ymidPx) ) }]

      set x2Px [expr { round( ($PxPerUnit * $y2) + $xmidPx ) }]
      set y2Px [expr { round( $curCanHeightPx - (($PxPerUnit * $z2) + $ymidPx) ) }]

      set x3Px [expr { round( ($PxPerUnit * $y3) + $xmidPx ) }]
      set y3Px [expr { round( $curCanHeightPx - (($PxPerUnit * $z3) + $ymidPx) ) }]

      set x4Px [expr { round( ($PxPerUnit * $y4) + $xmidPx ) }]
      set y4Px [expr { round( $curCanHeightPx - (($PxPerUnit * $z4) + $ymidPx) ) }]

It needs 589 chars.

Let's try with a shorthand $(...)

      set x1Px $(round( ($PxPerUnit * $y1) + $xmidPx ))
      set y1Px $(round( $curCanHeightPx - (($PxPerUnit * $z1) + $ymidPx) ))

      set x2Px $(round( ($PxPerUnit * $y2) + $xmidPx ))
      set y2Px $(round( $curCanHeightPx - (($PxPerUnit * $z2) + $ymidPx) ))

      set x3Px $(round( ($PxPerUnit * $y3) + $xmidPx ))
      set y3Px $(round( $curCanHeightPx - (($PxPerUnit * $z3) + $ymidPx) ))

      set x4Px $(round( ($PxPerUnit * $y4) + $xmidPx ))
      set y4Px $(round( $curCanHeightPx - (($PxPerUnit * $z4) + $ymidPx) ))

It needs 525 chars

Let's use a virtual "mathematical mode" construct :

    ⟦*⟧{ 
        x1Px = round, $PxPerUnit * $y1 + $xmidPx
        y1Px = round, $curCanHeightPx - $PxPerUnit * $z1 - $ymidPx

        x2Px = round, $PxPerUnit * $y2 + $xmidPx 
        y2Px = round, $curCanHeightPx - $PxPerUnit * $z2 - $ymidPx

        x3Px = round, $PxPerUnit * $y3 + $xmidPx
        y3Px = round, $curCanHeightPx - $PxPerUnit * $z3 - $ymidPx

        x4Px = round, $PxPerUnit * $y4 + $xmidPx
        y4Px = round, $curCanHeightPx - $PxPerUnit * $z4 - $ymidPx
    }

It needs only 479 chars.

Which one looks more readable ? more maintenable ?

See also


AMB - 2025-06-05 05:53:08

I am a bit late to the party, but what about [{math}]? Essentially, if the first word parsed in a Tcl command is wrapped in curly braces, it would treat that braced string as an expression, and any additional words in the command are then treated as an error. I checked the entirety of the library that comes with an ActiveState installation of Tcl, and there is no case where a command name is wrapped in curly braces. While this change could theoretically break backwards compatibility, I believe that it would have virtually no impact. Where a command does have spaces in it, you could call it with a variable that stores the command name or use a quoted string.

Example with the "expr" command (example from TIP#676 ):

.canvas addtag enclosed [expr {$x - 20}] [expr {$x + 20}] \
        [expr {$y - 20}] [expr {$y + 20}]

Example with the proposed [{math}] syntax:

.canvas addtag enclosed [{$x - 20}] [{$x + 20}] [{$y - 20}] [{$y + 20}]

Typing it out feels very familiar to people used to the expr command; the only difference is that you don't have to type "expr ", saving five characters per expression. Additionally, the similarity of this syntax with the "expr" command would make updating your code to this syntax really easy- just remove the word "expr" before all your braced expressions.

Here is what I imagine Tcl would look like with this change:

# What Tcl would be like with the [{math}] syntax
proc hypot {a b} {{sqrt($a**2 + $b**2)}}
puts [hypot 3 4]; # 5
puts [lmap x {1 2 3} {{$x * 2}}]; # 2 4 6
set a 5
set b [{$a + 10}]
puts $b; # 15

AMB - 2025-06-05 15:59:52

Here is a minimal proof-of-concept for how I imagine [{math}] syntax to work. The real implementation would have to be handled by modifying the Tcl command parser, this is just a hack.

# [{math}]
# A minimal proof-of-concept for a modified Tcl parser that eliminates most
# need to call the "expr" command.

# mathsub --

proc mathsub {body} {
    # For the start of every command that has its first word wrapped in curly
    # braces, insert the word "expr". Real implementation would be in the Tcl
    # parser, 
    regsub -all {(^|([\[\n;])\s*)\{} $body "\\1expr \{"
}

# mathcli --

proc mathcli {} {
    global userInput userInputComplete
    # Enter interactive mainLoop
    set oldFileEvent [fileevent stdin readable]; # Save old file event
    fileevent stdin readable ::GetUserInput; # File event for user input
    while {1} {
        # Initialize
        set userInput ""
        set userInputComplete 0
        puts -nonewline "% "
        flush stdout; # For normal Tcl
        # Wait for user input
        vwait ::userInputComplete
        if {$userInputComplete == 1} {
            # User input
            set code [catch {uplevel 1 [mathsub $userInput]} result options]
        }
        # Evaluate user input, but catch for error or other return codes
        switch $code {
            0 { # Success. 
                if {[string trim $userInput] eq ""} {continue}
                if {$result ne ""} {puts $result}
            }
            1 { # Error. Print the error message, do not pass error
                puts [dict get $options -errorinfo]
            }
            2 { # Return, return to caller
                # Lower the level of a return code (to account for this level)
                if {[dict get $options -code] == 2} {
                    dict incr options -level -1
                }
                break
            }
            3 { # Break, throw warning
                puts "invoked \"break\" outside of a loop"
            }
            4 { # Continue, throw warning
                puts "invoked \"continue\" outside of a loop"
            }
        }
    }
    # Exit interactive mathcli
    fileevent stdin readable $oldFileEvent; # Restore old file event (if any)
    # Return user specified code and result
    return -options $options $result
}

# GetUserInput --
#
# File-event on stdin to get user input. Uses userInputComplete for vwait

proc GetUserInput {} {
    global userInput userInputComplete
    # Get user input
    append userInput "[gets stdin]\n"
    if {[info complete $userInput]} {
        set userInputComplete 1
    }
    return
}

mathcli

Here's what it is like:

% set x 5.0
5.0
% set y [{$x + 1}]
6.0
% {$y - $x}
1.0
% set mylist [list $x [{$x + 1}]]
5.0 6.0
% lmap i {1 2 3 4 5 6 7 8 9 10} {
    if {$i % 2} {
        continue
    } else {
        {$i*10}
    }
}
20 40 60 80 100

It feels very natural, probably because I am used to typing expressions in braces, such as in the Tcl commands "if", "for" and "while", to name a few. The expr command would still have a purpose, for evaluating built-up expressions with double-substitution. But for all cases where double-substitution is not desired, a one-word command wrapped in curly braces would be evaluated as math.

Try it out for yourself, and let me know what you think!


NR - 2025-06-05 18:53:01

After discovering the extensive discussion on expr shorthand for Tcl9. I realize this has been a long-standing challenge in the Tcl community. My observation is that most proposals try to solve the problem by making expressions more compact, but they still feel like "Tcl with shortcuts" rather than evolving Tcl's fundamental expressiveness.

A Note on My Background

I should mention that I'm relatively new to the Tcl community and may not be fully aware of all the historical discussions and decisions that have shaped the language. If my proposal echoes ideas that have already been thoroughly discussed and ultimately disapproved, I apologize for any repetition. I'm still learning about Tcl's rich history and the reasoning behind past design decisions. That said, I hope my fresh perspective—coming from experience with modern languages—might offer a different angle on this long-standing challenge, even if the core concepts have been explored before.

Introducing the LET command with new operators - A Different Philosophy

Instead of another expr shorthand, I propose a modern assignment operator that brings Tcl closer to contemporary language ergonomics while preserving its core philosophy:

# Traditional Tcl (verbose)
set result [expr {$x * 2 + 1}]

# My proposal (modern assignment)
let result := {x * 2 + 1}  # No $ needed, clear intent

Key Innovations in My Approach

  • 1. Assignment-Focused Syntax

Rather than just shortening expressions, I introduce modern assignment operators inspired by successful languages:

:= - Normal assignment (inspired by Go's short variable declaration)
:~ - Lazy evaluation (inspired by Haskell and Scala)
  • 2. Intelligent Variable Substitution

My implementation automatically detects variables and adds $ prefixes, while protecting mathematical functions:

let area := {pi() * pow(radius, 2)}
# Becomes: pi() * pow($radius, 2)

Code :

#!/usr/bin/env tclsh

# ============================================================================
# LET command + new operators
# Supported syntaxes:
#   let var := {expression}     # Normal assignment (automatic $ substitution)
#   let var :~ {expression}     # Lazy evaluation (computed on first access)
# ============================================================================

# Global storage for lazy variables
set ::let_lazy_vars {}

# Preprocessing function to automatically add $ prefixes
proc ::let_preprocess_expression {expression caller_level} {
    # Mathematical functions that should not be prefixed
    set math_functions {
        abs acos asin atan atan2 ceil cos cosh exp floor fmod hypot
        log log10 pow sin sinh sqrt tan tanh max min rand srand
        double int round wide entier bool
    }
    
    # Get available variables based on context
    set existing_vars {}
    
    if {$caller_level == 0} {
        # Global level - get all global variables
        set existing_vars [info globals]
    } else {
        # Local level - combine local and global variables
        if {[catch {
            set local_vars [uplevel [expr {$caller_level + 1}] {info locals}]
            set global_vars [info globals]
            set existing_vars [concat $local_vars $global_vars]
        } err]} {
            set existing_vars [info globals]
        }
    }
    
    # Filter out system variables and math functions
    set filtered_vars {}
    foreach var $existing_vars {
        if {$var ni $math_functions && 
            ![string match "tcl_*" $var] && 
            ![string match "*_*" $var] &&
            $var ni {argc argv argv0 env auto_path}} {
            lappend filtered_vars $var
        }
    }
    
    set result $expression
    
    # Substitute variables with their $ prefixed versions
    foreach var $filtered_vars {
        # Check if variable appears as a whole word in the expression
        if {[regexp "\\m${var}\\M(?!\\s*\\()" $expression]} {
            # Explicit construction of replacement
            set replacement "\$${var}"
            set result [string map [list $var $replacement] $result]
        }
    }
    
    return $result
}

proc let {varname operator expression} {
    set caller_level [expr {[info level] - 1}]
    set processed_expr [::let_preprocess_expression $expression $caller_level]
    
    switch -exact -- $operator {
        ":=" {
            # Normal assignment - evaluate immediately
            if {$caller_level == 0} {
                set value [uplevel #0 [list expr $processed_expr]]
                uplevel #0 [list set $varname $value]
            } else {
                set value [uplevel $caller_level [list expr $processed_expr]]
                uplevel $caller_level [list set $varname $value]
            }
            return $value
        }
        
        ":~" {
            # Lazy evaluation - store preprocessed expression
            dict set ::let_lazy_vars $varname $processed_expr
            if {$caller_level == 0} {
                uplevel #0 [list trace add variable $varname read [list ::let_lazy_eval $varname]]
            } else {
                uplevel $caller_level [list trace add variable $varname read [list ::let_lazy_eval $varname]]
            }
            return "lazy:$varname"
        }
        
        default {
            error "Unknown operator '$operator'. Use := or :~"
        }
    }
}

# Lazy evaluation function
proc ::let_lazy_eval {varname args} {
    if {[dict exists $::let_lazy_vars $varname]} {
        set expression [dict get $::let_lazy_vars $varname]
        
        # Evaluate expression in appropriate context
        set caller_level [expr {[info level] - 2}]
        if {$caller_level <= 0} {
            set value [uplevel #0 [list expr $expression]]
            uplevel #0 [list trace remove variable $varname read [list ::let_lazy_eval $varname]]
            uplevel #0 [list set $varname $value]
        } else {
            set value [uplevel $caller_level [list expr $expression]]
            uplevel $caller_level [list trace remove variable $varname read [list ::let_lazy_eval $varname]]
            uplevel $caller_level [list set $varname $value]
        }
        
        # Clean up lazy storage
        dict unset ::let_lazy_vars $varname
        
        return $value
    }
}
# ============================================================================
# COMPREHENSIVE TESTS AND EXAMPLES
# ============================================================================

puts "=== LET Extension for Tcl - Complete Tests ==="

# Test variables
set x 5
set radius 3
set height 10

puts "\n1. Simple test"
let result := {x * 2 + 1}
puts "result = $result"

puts "\n2. Multi-line expressions"
let area := {
    3.14159 * pow(radius, 2) + 
    2 * 3.14159 * radius * height
}
puts "area = $area"

puts "\n3. Function test"
proc max {a b c} {
    set result $a
    if {$b > $result} {set result $b}
    if {$c > $result} {set result $c}
    return $result
}
let maximum := {max(10, 25, 15)}
puts "maximum = $maximum"

puts "\n4. Lazy evaluation test"
set counter 0
proc expensive_calc {} {
    global counter
    incr counter
    puts "  -> Expensive computation executed (call #$counter)"
    return [expr {$counter * 100}]
}
let lazy_result :~ {[expensive_calc]}
puts "Lazy variable created (not yet evaluated)"
puts "First access: $lazy_result"
puts "Second access: $lazy_result (cached)"

puts "\n5. Complex expression test"
set a 2
set b 3
let complex := {sqrt(pow(a, 2) + pow(b, 2)) + a * b}
puts "Complex expression = $complex"

puts "\nFinal syntax:"
puts "  let var := {expression}    # Assignment without \$"
puts "  let var :~ {expression}    # Lazy evaluation"

puts "\n=== Benefits ==="
puts "Modern syntax inspired by Go, Python, Haskell"
puts "Automatic variable substitution"
puts "Lazy evaluation for performance optimization"
puts "Maintains Tcl's philosophy with {} braces"
puts "Clean and readable mathematical expressions"

This extension represents a working proof-of-concept that could be refined based on community feedback... Voilà


AMB - 2025-06-05 19:33:27

There is an active Tcl TIP (TIP 674) for a similar "let" command, although it does not modify the grammar of expr like you mention. If you don't like using "$" to denote variables in math expressions, I recommend checking out the VecTcl package and its "vexpr" command.


NR - 2025-06-05 20:47:22

Thanks AMB for the links, I didn't know TIP 674, but I did know VecTcl package. What I've noticed with Tcl is that is not Everything is string ! but Everything exists but not often in the core of TCL, it is perhaps the basis of an interpreted language.In my simple code here I wanted to show that for me, it may not be the case for everyone, the addition of the ‘$’ is annoying, which is what I find more annoying than the fact of reading the Tcl expr.If I had to sort this by type, I would delete the ‘$’ first and then the Expr.I support you 100%, for the modernisation of calculations in Tcl, whatever the decision of the members of the TCT for your TIP, thank you for making things change. I don't have much optimism (If I refer to the first date of this thread) that your TIP, TIP674 or any TIP wishing to modify or replace the command expr will be introduced in Tcl9, perhaps Tcl10,11. Anyway, all the best.


AMB - 2025-06-05 22:55:41

Hi NR, I haven’t submitted a TIP yet but I’m gonna take a stab at it this weekend.

I agree that the $ is a bit obnoxious when writing math expressions, but so many things would break if the math interpreter parsed bare words as variable substitution. For starters, math commands are bare words with parentheses, and would be functionally indistinguishable from accessing a Tcl array. For example, if you had an array named “abs”, would abs(-2) return 2, or the value stored in the array with the key “-2”? It is an edge case, but some decision would have to be made regarding whether to give the math command or array priority.

Personally, I think a shorthand or special delimiter for expr should be tackled first before changing the actual expr parser. It is a much more achievable goal and would be less likely to break existing code.


AMB - 2025-06-06 20:04:29

Based on discussions in Single quotes to denote expr (admittedly a discussion that has evolved far beyond the original title), I think that instead of implementing a shorthand for expr, a new math parser should be added that does not do variable substitution with dollar signs. Instead, it would interpret bare words that are not numbers, math functions, or other reserved words as references to variables. It could be called "math" or something, and have a shorthand prefix {$}{...}. This could be implemented without breaking any existing scripts, and would provide more readable math notation in Tcl.

Example of what it would look like:

set x 1.0
set y {$}{x+1}; # or you could do "set y [math {x+1}]"
set z {$}{2*(x + y)}
puts {$}{z**2}; # 36

I think Tcl would be a lot more popular if this was implemented.


q3cpma - 2025-06-08

Am I the only one disliking any of these infix "solutions" to a problem that doesn't really exist since we got TIP#174 ? A language that proudly proclaims "everything is a command" should follow Lisp and have the audacity to make them the standard way of doing math, not continue to worship that historical notation divide between functions and operators (which are just functions).


AMB - 2025-06-08 05:50:31

RE: q3cpma So, you are saying that people should essentially have the following at the start of every Tcl script?

namespace import tcl::mathop::*
namespace import tcl::mathfunc::*
interp alias {} = {} expr

I guess for the situations where "expr" is overly obnoxious, like for simple adding of two numbers or for doing a single math operation, the code [+ $num1 $num2] or [abs $x] is about as succinct as you can get. And the alias "=" can trim a few characters off for more complicated expressions where polish notation can be a bit hard to read. Also the alias "=" only ends up being one more character than a {=}{math} prefix, so it wouldn't really have much of a benefit.


MG My biggest issue with "anything that's not a math function is a variable" is you can then never implement another math function in a backwards-compatible way, because someone may have - validly, at the time - used that name as a variable name before it was reserved. I've run into those kinds of issues in other programming languages and it's a pain to navigate after the fact.


AMB - 2025-06-08 17:23:43

Honestly this whole conversation is making me appreciate the design of Tcl more. It may be strict, but at least there are no surprises.

My only complaint then is that using an alias for expr is significantly slower than just calling expr. It would be nice if the “=“ prefix was added to the tcl::mathop namespace so that it could be an equally fast alternative to expr.


AMB - 2026-02-11 16:15:22

The expr command is, in my opinion, one of the main turn-offs of Tcl. Math should be cleaner. Some people try to reduce the obtuseness of the command by creating an alias, such as "=", but this only reduces three characters per math expression. It is still ugly.

set x 5.0; # looks great
set y [expr {$x * 2}]; # ugly
interp alias {} = {} expr; # define an alias to make things better
set z [= {$x + $y}]; # still ugly :(

For the set command, I have proposed modifying the syntax to be able to handle math expressions (see the page "set varName = expr")

In this case, you could do something like this:

set x 5.0; # looks great
set y = {$x * 2}; # awesome
set z = {$x + $y}; # super cool

You could also replicate this pattern with other commands such as the lset command. But the expr command is not just used in defining variables or elements in lists. For other situations, such as entering multiple expressions as arguments for a command, this change to the syntax of the "set" command doesn't really help out. See the example below:

.canvas create rectangle [expr {$w-100}] [expr {$h-40}] [expr {$w+100}] [expr {$h+40}]; # This is obtuse
interp alias {} = {} expr; # define an alias to make things better
.canvas create rectangle [= {$w-100}] [= {$h-40}] [= {$w+100}] [= {$h+40}]; # This is still obtuse

For these situations, it would be great if Tcl had a prefix {=} that works like the expansion prefix {*}. But, rather than parsing a list and then expanding the arguments, it would take a series of comma-separated expressions, exactly how multiple arguments are parsed in a math function.

Then, the canvas example can be cleaned up as so:

.canvas create rectangle {=}{$w-100,$h-40,$w+100,$h+40}; # beautiful :')

I believe that these two changes to Tcl would make the user experience much better. This pain point has persisted for too long.


NR - 2026-02-11 20:35:39

Hi AMB, If possible, can you add your proposition here A Better way to do calculations .I love your solution, of course, but it would be even better without the dollar sign :). I already mentioned this on the page above. Thanks in advance.


AMB - 2026-02-11 22:44:15

Hi NR, I just added it! And I know, it would be nice if we didn't have to use the dollar sign for variable access in math expressions, with the exception of arrays, which should still have a dollar sign (in order to not conflict with the mathfunc notation), and variable names with spaces in them (to differentiate from strings).


CGM - 2026-02-12

My revised TIP 676 is now implemented and I hope to get a TCT vote on it in the next month or so. It allows the example above to be written as:

.canvas create rectangle [= w-100] [= h-40] [= w+100] [= h+40]

AMB - 2026-02-12 14:13:05

Hi CGM, congrats on implementing the TIP! I also am going to implement a TIP for the {=} expansion prefix. Beyond one argument, even with a dollar sign for variable access, it is shorter to write than your TIP, and gets even shorter with more arguments.

{=}{$w-100,$h-40}
[= w-100] [= h-40]

I like the idea of using bare words for variable access in math, but why can’t it be implemented directly in the expr command? You could just have a limited selection of variables that could be accessed with their bare word (arrays would have to be accessed with the dollar sign, and variables with spaces or special characters as well).

The reason I say this is because the expr engine is already used for things like the conditional statements for control structures such as if, while, and for. So, adding a separate math engine to the core that differs in style to the one used in control structures would be confusing in my opinion. Why can’t the expr engine be changed to allow a limited selection of bare word variable substitution? It would be backwards-compatible and would allow you to just create an “=“ alias to the expr command to replicate your TIP.

Furthermore, if that change was made to how expr treats barewords, my math expansion notation would be even more succinct:

{=}{w-100,h-40}
[= w-100] [= h-40]

FM : Hi AMB. The little annoying thing with {=}{...} is that it will work only at the begining of a word. It won't work in some other situations :

puts $Array({=}{$w-100}); # won't work
puts $Array([= w-100]); # will work

This is because an array index substitution goes through parseToken, which is not sensitive to the word analysis.

Hi CGM. I'd like to congratulate you either. It's really big to create a parser like this. But, did you make some benchmark ? How does its speed compare to the one of expr ? Sadely, I've no time to make my idea on the subject for the moment. Maybe you remember it ? Previously, I tried a [(...)] substitution with the synthetic string trick of Eric Taylor, calling expr under the cover. To nest it, I finally fond out that it was possible to use the TCL_TOKEN_SUBEXPRESSION Token to mark a math substitution syntax. It was working, I could write :

set x [(1+[(1+1)])]; # ok, it's very silly, but language needs sillynessproof...

And I really didn't have to make a lot of change to get it.

As you know, the synthetic string approach is not working because of the pointer arithmetic Tcl use, as Eric Taylor explained. But I was thinking that instead of using the synthetic string approach into parseToken, I could maybe use a TCL_TOKEN_SUBEXPRESSION approach instead. This way, we can use the expr parser. But, the better would be to make it multiline, and list-aware.

# Current :
.canvas create rectangle [expr {$w-100}] [expr {$h-40}] [expr {$w+100}] [expr {$h+40}] 
set y [expr {$x*2}]
set A([expr {$x*2}]) 1
# FM Proposal 
.canvas create rectangle [($w-100, $h-40, $w+100, $h+40)] 
set y [($x*2)]
set A(($x*2)) 1

# AMB proposal
.canvas create rectangle {=}{$w-100, $h-40, $w+100, $h+40}
set y = {$x * 2}
# set A({=}$x*2) 1; won't work

# CGM Proposal
.canvas create rectangle [= w-100] [= h-40] [= w+100] [= h+40]
set y [= x*2]
set A([= x*2]) 1

I prefer my (FM) proposal, because :

  1. all this is about to have a new substitution behaviour, not to have a new command (what = is).
  2. Consequently, a new switch to subst come very logically : subst -noexpression {}. Just ignore the TCL_TOKEN_SUBEXPRESSION and mark it as TCL_TOKEN_TEXT.
  3. Contrary to the {=}prefix, this will work inside a word, and inside in array index.

AMB - 2026-02-13 16:52:59

Hi FM,

I agree that the {=} prefix wouldn't work in that case. In those cases the expr command would still be needed, or you could alias it to "=". However, these edge cases are relatively small. The combination of "set varName = expr" and the {=} prefix should cover most cases of math in Tcl.

I have a few issues with the notation [($a + $b, $c + $d)]:

  1. It conflicts with "proc (*". I don't know why someone would create a procedure with this name, but it would nevertheless represent a backwards-compatibility issue. The {=} prefix currently returns an error, so adding it could be done without breaking anything. FM answer : I don't think it should be advised to use pair-chars to name procs. There is backslash substitution anyway./FM
  2. It would violate rule 5 of the Dodekalogue, which only permits argument expansion with {*} (my proposal would only extend this rule to add the math prefix {=}). FM answer : expr shorthand is not link to the concept of argument expansion IHMO. /FM
  3. It would violate rule 6 of the Dodekalogue, because it would not perform substitution on the contents of [(...)] before passing it to the expr engine. FM answer : substitutions are done through the usual expr parser engine. It avoids the double substitution problems. /FM
  4. It would violate rule 7 of the Dodekalogue, because it would create an exception to the interpretation of square brackets. FM answer : Yes it's a new rule, a new kind of substitution, the mathematical expression substitution. /FM
  5. From a perspective of being "shorthand", the [(...)] notation is only one character shorter than the {=}{...} notation. Additionally, because the {=} prefix would operate the same as the {*} prefix (with the exception of pre-processing the "word" with expr), you could apply it to the result of a command or to the value of a variable. You cannot do the same with [(...)]. The [(...)] notation is more limited. FM answer : but won't we fall into the double substitution problem then ? /FM
[expr {$a + $b}] [expr {$c + $d}]; # where we are at right now
[($a + $b, $c + $d)]; # FM's proposal
{=}{$a + $b, $c + $d}; # AMB's proposal (only one more character)
{=}[mycommand arg arg]; # Also works with expanding results of a command
{=}"$expr1, $expr2"; # Also works with quoted strings
{=}$comma_separated_expressions; # Also works on entire variables

FM answer : it's very dangerous IHMO. {*} will never make any substitution on its suffix. It is just changing the argument's arity in a command. How many rounds of substitution in such creative writing ? If {=} is similar in shape to {*}, the behaviour is really different. That's because to evaluate an expression relys more to substitute it than to expand it. /FM

All this being said, I understand that it still does not address the fact that you would have to use the expr command in string substitution. For this edge case, the popular alias "=" to the expr command would be helpful. So, here is your example, but with the addition that I think that the "=" sign should be a built-in alias for expr.

# Current :
.canvas create rectangle [expr {$w-100}] [expr {$h-40}] [expr {$w+100}] [expr {$h+40}] 
set y [expr {$x*2}]
set A([expr {$x*2}]) 1

# FM Proposal 
.canvas create rectangle [($w-100, $h-40, $w+100, $h+40)] 
set y [($x*2)]
set A(($x*2)) 1

# AMB proposal
.canvas create rectangle {=}{$w-100, $h-40, $w+100, $h+40}; # new {=} expansion prefix
set y = {$x * 2}; # modification of "set" syntax
set A([= {$x*2}]) 1; # with alias "=" to expr

# CGM Proposal
.canvas create rectangle [= w-100] [= h-40] [= w+100] [= h+40]
set y [= x*2]
set A([= x*2]) 1

I really think that the edge case of "set A([expr {$x*2}]) 1" is so rarely done (I have never seen it) that focusing on it is not productive. I think the main criteria for implementing a new expr shorthand are:

  1. Backwards compatibility. We are already in Tcl9, so the changes should not break the interpreter at all.
  2. No new rules. It shouldn't alter, at a fundamental level, the basic rules of Tcl.
  3. Ease of implementation.

I do like CGM's implementation, although I think that it could easily just be an optional package, configured with an alias. Like, calling the command "math" or "calc", and then allowing the user to assign an alias. A good example of this is the VecTcl package, which has similar math notation to what CGM is proposing. Honestly, I think it would be best to have it be a separate package, as it would be confusing to have two separate math notations in the same language.


AMB - 2026-02-13 20:58:04

Excuse me, FM, but how is having {=} as an expansion prefix that also evaluates a math expression "dangerous"?

Yes, {*} doesn't make any substitution on its suffix. But {=} would, and then it would also expand the result. If the word that followed was in braces, it would be basically identical to calling expr with a braced expression, otherwise, if you used a quoted string, it would perform double-substitution, the same as with the expr command. It isn't dangerous, it is essentially identical to the behavior of expr.

Additionally, I am very confused by your comment, because you yourself have proposed radical changes to Tcl using braced prefixes. In Expanding the Expansion prefix - Concept of protocol, you proposed that not only could a "protocol" (braced prefix) pass the suffix through a command (such as expr), but it could also modify the behavior of the interpreter for the following words. Criticizing my idea on this basis is hypocritical, for you have proposed far more radical extensions of the expansion prefix.

FM answer : You should refer the double substitution or injection Attack or Brace your expr-essions pages to see why this can be dangerous.

The Tcl parser split commands in Tokens. While parsing, each command is splited in TCL_TOKEN_WORDs. Those words can be composed of TEXT, VARIABLE or (nested-)COMMANDs Tokens. There is 3 kinds of WORDS : WORD (generic case), SIMPLE_WORD (only composed of TEXT components, a litteral), EXPAND_WORD (to be expanded later). After that parsing, each WORD Token is substituted : COMMANDs TOKEN are evaluated (command substitution), VARIABLEs are substituted (variable substitution), TEXTs are taken as they are. Then, generally, all those substituted Tokens are appending in one single Tcl_Obj, except if the WORD is mark to be EXPANDed (in this latter case, it will be expanded as a list to generate an array of Tcl_Obj).

My proposal in Expanding the Expansion prefix - Concept of protocol is to create a new Token type, whose name is PREFIX. This is different from {*} or {#} which would be hardcoded. When the substitution step will encounter this PREFIX Token, it will create a special type of Tcl_Obj, a TclMetaType Tcl_Obj, whose internal rep will be made of 2 Tcl_Obj, one beeing the header (the prefix term), the other one beeing the encapsulated data (the suffix). This TclMetaType object will then be given to the command array of object which is beeing evaluated.

The notion of protocol came to configure the commands on how to operate when it enconters a TclMetaType Tcl_Obj. If a command has no protocol on how to handle a specific argument which is of TclMetaType, it will simply use the suffix value (the encapsulated data), ignoring the prefix value. One example was for the set command. As set is waiting for a variable name as its first argument, any prefix on it, which carries meta-data on this value, could indicate a type, that set could use to modulate its behaviour. So I imagined the notion of protocol to specify effects which will occur only into one specific Tcl command.

Of course, the mathematical expression substitution can be seen as a special protocol, but it occurs at the level of the Tcl_Parser. At this level, protocols which generates values from text are named « substitution » (of command or variable type), whereas the protocol which is changing the arity of command words is named « expansion prefix » ({*}). The futur {#} prefix could be named « annihilation prefix » while any other braced-prefix (according to my proposal) could be name « encapsulation prefix ». Those last three prefixes don't produce any new value, they only change the way those values are put together before to be given to a command.

On this point of view, the « expr shorthand » at the Tcl parser level is of kind « substitution » because it has to produce a new value which not only consist in appending strings together. That's why I saw it as a new kind of substitution (so, a new Tcl rule), The « mathematical expression substitution » rule. For this, I was planing to use a Token Type (which exists previously, maybe for historical reasons), the SUB-EXPRESSION Token. Then, during the substition step, instead of calling Tcl_eval, like it is for commands, or Tcl_ObjGetVar2, like it is for variables, Tcl will use directely the expr parser to create the resulting Tcl_Obj.

Here are the reasons of my point of view. You can see that nothing is hidden in it. I prefer to reserve « braced-prefix » syntaxes to make those things which will only change the « shape » of values, not changing the values themselves.

As « mathematical expression substitution » looks to me more like command substitution, I choose to use this [(...)] syntax, in a command line context, but I had to make it as a $var((...)) syntax in a variable index context, mostly for esthetical reasons. In this model, the « mathematical expression substitution » rule is realized in 2 distinct ways, accordingly to the context (command line / variable index). The common point between the two constructs beeing that if a nested command or a variable index begin with ( and finish by ), then it will have to be substitued as a « mathematical expression ».


CGM - 2026-02-14 Answering a couple of points from above

FM asks how the speed of = compares to expr. I posted timings on the Tcl-core list, see https://sourceforge.net/p/tcl/mailman/message/59266474/ and https://sourceforge.net/p/tcl/mailman/message/59272682/ . For most cases the speed of = is close to that of braced expr.

AMB suggests that my implementation could just be an optional package. There are two reasons for putting it in the core. One is that I use much of the existing expr infrastructure for lexical analysis etc. and this code is not accessible to extensions, so an extension would need to duplicate it. The other reason is that = is now byte-compiled for speed and it's not currently possible to byte-compile commands defined in extensions.

FM answered : We can't say [= ...] is a expr shorthand. [= ...] redefine how mathematical expressions are parsed, using another DSL. Your tests show that it's as fast as expr, at least when the compilation succeed. To succeed with array, you need to use separate arguments, so that the value of the array element can be substitute by the Tcl_parser itself, using usual variable substitution rule. In effet, getting rid of the $ sign in this case, would have leaded to an amiguity with the function syntactical construct func(arg, args)

Well, if you think admittable that the variable syntax in math expression can change, why not go a step further, and admit the function syntax can change also ? Let's say, there is no need to enclose function arguments into pair of (...) and we just have to separate the func name and its arguments with a new symbol, for instance °. Ex (with a native list handling implemented) :

lassign [= R([= i+1]) * (cos ° angle), R([= i+1]) * (sin ° angle)] M([= i+1],x) M([= i+1],y)
set R([= i+2]) [= sqrt ° (pow ° M([= i+2],x), 2 + pow ° M([= i+2],y), 2)]

Then, the problem of ambiguity between arrays and functions would desapear. And you would be able to compile the array access as well !

And I could choose to plug my substitution shorthand to htis = parser rather than to the expr one.

lassign [( R((i+1))*(cos ° angle), R((i+1))*(sin ° angle) )] M((i+1),x) M((i+1),y)
set R((i+2)) [( sqrt ° (pow ° M((i+2),x), 2 + pow ° M((i+2),y), 2) )]


AMB - 2026-02-14 19:25:37

I understand the purpose of bracing expressions for security (I always brace my expressions), and in that case, the notation {=}{...} is just as secure as [expr {...}]. All I was pointing out is that you could also have double substitution if desired, with passing the result of a command (a command that returns an expression to be evaluated). Calling [expr $my_expression] is perfectly allowed, and safe if done right.

With the notation [(...)], would it perform any subsitution on the contents prior to evaluation? Normally, only {text in curly braces} avoids substitution when being parsed.
FM - Answer : It won't. The detection is done into the procedure parseTokens, as a new case into the '[' branch of the loop, by checking if the detected '[' is followed by a '('. Then it's using a slightly modified expr parser to get the length of the string till the last ')', the one before the closed ']'. The whole string is then marked as TCL_TOKEN_SUBEXPR. It will be substituted, or compiled, later, in one step, when the time has come to get its value, with the expr related procedures. So, there is no double substitution : All the range of char inside [(...)] is taken as a block, marked as TCL_TOKEN_SUBEXPR, which has no components, in former analogy with what is done for a TCL_TOKEN_COMMAND (check the comments in tcl.h). The only difference is that, during substitution step, a Tcl_Expr* procedure will be called instead of a Tcl_Eval* procedure. This way all Variable, Backslash or Commands which are contained into the expression will be parsed, and substituted, or compiled, by a Tcl_Expr* procedure. That's why I said it's a new rule, a new kind of sustitution, the mathematical expression substitution. Notice I still have to implement it into the index part of a variable name.


FM - 2026-02-17 Hi AMB, CGM, Tclers. I have posted some code on github . The [(...)] shorthand is working well. The index syntax Array((expression)) is only partly working : it won't work in a tclsh interactive session (tailcall crash), but it will work if you exec a script with Tclsh on the command line.
The good news is that to use [(...)] shorthand is generally faster than to use [expr {...}] command. I imagine it's because Tcl doesn't need to go through Tcl_Eval interface any more. The changes are somehow minimalist. If you want to check and test and give feedback, I'd be pleased
Here some code for illustration :

proc ScalarProduct {U V} {
    set R 0
    foreach u $U v $V {
        set R [($R+$u*$v)]
    }
    return $R
}

proc CrossProduct {U V} {
    lassign $U x y z
    lassign $V u v w
    
    return [list [($y*$w - $v*$z)] \
                [($z*$u - $w*$x)] \
                [($x*$v - $u*$y)]]                
}

# primes numbers
proc sieve {n} {
    set L [lseq $n]
    for {set i 2} {$i < $n} {incr i} {
        set j $i
        while {$i*$j < $n} {
            lset L [( $i*$j )] {}
            incr j
        }
    }
    return [lsearch -all -not -exact $L {}]
}

CGM - 2026-02-17 Interesting. FM, your C code is shorter than mine for TIP 676, also it has advantage of maintaining the exact same expression language. However it requires a change to the dodekalogue, which for many people is a big deal.

My TIP 676 proposal can be more compact, e.g. set R [= R+u*v] instead of set R [($R+$u*$v)]. Also the implementation is isolated in a single new command, with no impact on the dodekalogue or the general Tcl parsing code. These are the tradeoffs to be considered.

<<FM>> - answer : Well, it's adding one rule rather than changing the rules. Twodekalog will become Tridekalog, with this new substitution rule : "math expression substitution". There is two level to be distinguished : The syntax at the Tcl Parser level, the syntax of the infix math language DSL.

It is avantageous to have a specific syntax at Tcl Parser level : The tests with my proptotype shows that [(...)] is allways faster than [expr {}]. It's because [(...)] doesn't need to go through the command interface : It doesn't need to call recursively the Tcl parser. It doesn't need to locate a command in a hash table. As the expression is immediately Tokenized as an EXPRESSION Token, Tcl will use immediately expression related API to build the value. That's why it's faster. We can guess that the same speed difference will be true between [(...)] and [= ...], because [= ...] still has to make a recursive call of the Tcl parser, to locate the command in a table,...

The question of what infix math language DSL should be used is of interest too. [(...)] could as well use the one of = in the background.

So there should be two different TIP domain here : The parser shorthand (foreground) / The math DSL to use (background). I'd like, in the DSL, to have a native list capability, and I'm not against to remove the $ sign, since we know we are in the specific context of math language. The crossProduct example written above would then be written :

proc CrossProduct {U V} {
    lassign $U x y z
    lassign $V u v w
    
    return [(y*w - v*z, z*u - w*x, x*v - u*y)]                
}

Yes, that's a lot more clear ! But it will remain the problem of the ambiguity between mathfunc call and array access. Three possibilities here : In the math DSL, either change the function call syntax, either change the array syntax, either forbid arrays acces.

Too loop with an other proposal Expanding the Expansion prefix, I would finaly suggest a braced prefix for access of every kind of variable collection :

set Point [list 100 200]
set scale(Draw1) 10
set Orig [dict create X 0 Y 0]
set P [( 
    {array}scale("Draw1") * ( {list}Point(0) - {dict}Orig("X") ), 
    {array}scale("Draw1") * ( {list}Point(1) - {dict}Orig("Y") ) 
)]

<</FM>>

CGM By the way, this page has become too big for the wiki to diff, so perhaps we should split it?


AMB - 2026-02-18 17:47:59

Re: FM

Nice implementation! I understand now what you are saying and I see the value in having a special syntax to invoke expr without double substitution. I also like that it keeps the same native expr syntax. I think that is appropriate for Tcl. It would be nice to do math without using the dollar sign for variables, but it would not be visually consistent with the syntax of common control structures.

If you wanted double substitution for some reason (like making custom control structures that take an expression as an input), you could still explictly call "expr", but then this [( ... )] syntax would take over the majority of use cases.

If it could also expand the results, with commas separating the arguments, I would be in full favor of it, especially since you have already made headway on an implementation.

Edit: I take back the idea about automatically expanding the results. Instead, it should return a list, with the elements of the list separated by commas in the expression. Then you can expand that list.

# Current :
.canvas create rectangle [expr {$w-100}] [expr {$h-40}] [expr {$w+100}] [expr {$h+40}] 
set y [expr {$x*2}]
set A([expr {$x*2}]) 1

# Proposed :
.canvas create rectangle {*}[($w-100, $h-40, $w+100, $h+40)]; # looks pretty nice
set y [($x*2)]
set A([($x*2)]) 1

Then, the cross-product function can be written as this:

proc CrossProduct {U V} {
    lassign $U x y z
    lassign $V u v w
    return [($y*$w - $v*$z, $z*$u - $w*$x, $x*$v - $u*$y)]
}

<<FM>> : I totally agree with this native list handling syntax <</FM>>

Edit2: One other question I have for you, FM: You mentioned that your implementation does not recursively call the Tcl parser. Why not? Is this not allowed?:

set mylist {1 2 3}
set i 0
set x [( [lindex $mylist [($i + 1)]] * 2 )]; # 4

<<FM>> This is allowed, and it works (The command [lindex ...] is resolved by the expr parser). Let me explain better :
When you type this script : cmd $var [expr {expression}]

  • Tcl_parseCommand splits the script into 3 words tokens : the first is marked as Text, the second as variable, the third as command.
  • substToken loops on the words to give them values.
    • First word is simple text cmd : It is taken as this.
    • Next word is variable $var : it is resolved with Tcl_GetVar.
    • Last word is command expr {expression}.
      • Tcl_Eval is called recursively
        • Tcl_ParseCommand split command script into words and tokens
        • substToken loops on the words to give them values.
          • First word is simple text expr
          • second word is taken as a text {expression}
        • Evaluation step : expr command is located, evaluated with its argument -> value returned by Tcl_Eval.

Now, if you type this script : cmd $var [(expression)]

  • Tcl_parseCommand splits the script into 3 words tokens : the first is marked as Text, the second as variable, the third as expression.
  • substToken loops on the words to give them values :
    • First word is a simple text cmd : It is taken as this.
    • Next word is a variable $var: the value is resolved with Tcl_GetVar.
    • The last is Sub_expression expression: the value is resolved with Tcl_ExprObj

ie : There is no nested evaluation anymore. <</FM>>

Edit3: I am imagining that [( ... )] would be shorthand for expr* , a version of expr that I prototyped that returns a list after performing an expression. In that case, the [( bracket should work like a curly brace. As in, you don't need to escape new-lines.

Then, while this is a bit verbose, you could do multi-line expressions that return a list (and you could have comments within it)

proc CrossProduct {U V} {
    lassign $U x y z
    lassign $V u v w
    return [(
        # first element
        $y*$w - $v*$z,
        # second element
        $z*$u - $w*$x,
        # third element
        $x*$v - $u*$y
    )]
}

<<FM>> Well, multiline handling is already here :

puts [(
   1
   +
   1
)]

doesn't complain... and returns 2.
It's because Tcl_Parser is looking for the closed ] : What will remain is about improving expr parser. <</FM>>


AMB - 2026-02-19 02:31:30

The fact that your implementation of [( math )] doesn’t complain about newlines feels wrong lol. I understand how it works, but not escaping newlines within square brackets is normally not allowed, so it looks weird. I see no reason why Tcl has this limitation however. That’s why I made the page Newlines in Command Evaluation.

<<FM>> try this :

puts [
   # init the vars
   set x 1
   set y 2
   set z 3 
   # return the list
   list $x \
        $y \
        $z
]
# return : 1 2 3 (last result in the script)
# But :
puts [
   set x 1 \
   set y 2 \
   set z 3 
]
# error : wrong # args: should be "set varName ?newValue?"

It doesn't look weird, it's the way it works. Not escaping newlines is allowed in script substitution, as a command separator.

Similary, that is allowed in math substitution, but command separator should be « ; ».

puts [(
   # init the vars
   x = 1; 
   y = 2;
   z = 3;
   # return the list
   $x,
   $y,
   $z
)]

<</FM>>


AMB - 2026-02-26 23:27:33

To add to FM’s syntax, I propose that if the first word of a command begins with an open parentheses, it then looks for the close parentheses and evaluates everything inside as an expression. It would also require that there is no other word after it. This would replicate the [(math)] syntax, while also being more flexible.

set x 5.0
set y [ ($x*2) ]; # allows for whitespace 
lmap value [list $x $y] {($value+1)}; # 6.0 11.0
(
$x > 2 ? [puts hello] : [puts goodbye]
); # multiline allowed (acts like curly braces)

set (key) 10; # still works, (key) isn’t the first word, so it isn’t evaluated as a math expression. 

($x + 2) foo; # throws an error, word encountered after math expression

If for some reason someone had defined a command that started with a parentheses, it could still be called as shown:

proc (foo) {bar} {
concat $bar baz
}
{(foo)} hello; # hello baz

<<FM>> I add the first capability (the one above with lmap, not the one with the first word because of collision between expr and Tcl syntax) in Shorthand Expr TIP .

This new {(...)} shorthand will work with any of the arguments where a command is expecting a script : if, while, for, foreach, lmap, eval, apply, proc, ...etc

It completes usefully the formers. I think that now the proposal is fully coherent. It includes

  • Inline shorthand : [(...)]
  • Index shorthand : set array((...))
  • Script shorthand : eval {(...)}
  • Native list capabilities : [("a", "b", "c")]
  • Variable assignement with bareword on left side : [(i = j = 0)]
  • Multiple intructions : [(i=0; j=$i+1)]

The three latters can be used with each of the three formers

For example, now I can write :

proc determinant {M} {
    set MAP {{a b c} {d e f} {g h i}}
    lmap row1 $MAP row2 $M {
        lmap var $row1 val $row2 {($var = $val)}
    }
    return [($a*$e*$i + $b*$f*$g + $c*$d*$h - $c*$e*$g - $b*$d*$i - $a*$f*$h)]
}

proc crossProd {U V} {
    set uVars {u v w} 
    set vVars {x y z}
    
    lmap uVar $uVars uVal $U \
        vVar $vVars vVal $V {(
       $uVar = double($uVal);
       $vVar = double($vVal)
    )}
    return [($v*$z-$y*$w, $w*$x-$u*$z, $u*$y-$x*$v)]
}

puts [crossProd {1 2 3} {3 2 1}]
# -4.0 8.0 -4.0
proc tensorProd {U V} {
    lmap u $U {
        lmap v $V {(
            $u * $v                   
        )}
    }
}

puts [tensorProd {1 2 3} {3 2 1}]
# {3 2 1} {6 4 2} {9 6 3}

proc tcl::mathfunc::lmap {args} {
    tailcall ::lmap {*}$args
}

proc dotProd {U V} {(
     dot=0;
     lmap("u", $U, "v", $V, {(
        dot = $dot + $u * $v 
      )}); # end lmap "u"
     $dot
)}

puts [dotProd {1 2 3} {3 2 1}]
# 10

All this will make maths a lot easier IMHO,... and a lot of wiki pages to be updated and simplified<</FM>>